package org.monitor.model;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ServerMonitorManager {
	
	private static Logger logger = Logger.getAnonymousLogger();
	
	//未指定监控对象时的默认名
	private static final String ANONYMOUS = "anonymous";
	//监控日志存储目录
	private static final String HOME_DIR = "/monitorlogs/monitors/";
	private static ServerMonitorManager instance = new ServerMonitorManager();
	
	private static SimpleDateFormat dateFormater = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	
	//监控时长
	public static int EXPIRED = 3600000;//1小时
	//下一次检查时间
	private long checkTime;
	
	private ThreadPoolExecutor executor;
	//监控对象
	private Map<String, Date> monitorMap;
	//日志操作
	private Map<String, BufferedWriter> writerMap;
	
	private ServerMonitorManager(){
		executor = new ThreadPoolExecutor(0, 1, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<Runnable>());
		monitorMap = new ConcurrentHashMap<String, Date>();
		writerMap = new ConcurrentHashMap<String, BufferedWriter>();
		setCheckTime();
	}
	
	public static ServerMonitorManager getInstance(){
		return instance;
	}
	
	/**
	 * 开始监控
	 * @param monitorObj
	 */
	public void startMonitor(final String monitorObj){
		if(inMonitor(monitorObj)){
			return;
		}
		
		monitorMap.put(getMonitorObj(monitorObj), new Date());
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					String newMonitorObj = getMonitorObj(monitorObj);
					
					File file = getMonitorLog(newMonitorObj);
					if(file.exists()) {
						file.delete();
					}
					file.createNewFile();
					writerMap.put(newMonitorObj, new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true), "UTF-8")));
					
				}catch(Exception e){
					logger.log(Level.INFO, "", e);
				}
			}
		});
	}
	
	/**
	 * 记录监控信息
	 * @param monitorObj
	 * @param msg
	 */
	public void log(final String monitorObj, final String msg){
		checkMonitors();
		
		if(!ANONYMOUS.equals(getMonitorObj(monitorObj))){
			//监控对象操作时，同时记录一份匿名操作
			log(ANONYMOUS, msg);
		}
		
		if(!inMonitor(monitorObj)){
			//判断监控对象是否有被监控
			return;
		}
		
		final String timeStr = dateFormater.format(new Date());
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					String realMonitorObj = getMonitorObj(monitorObj);
					
					BufferedWriter writer = writerMap.get(realMonitorObj);
					if(writer != null){
						writer.write("//log time : " + timeStr + "\r\n" + msg + "\r\n");
						writer.flush();
					}
				}catch(Exception e){
					logger.log(Level.INFO, "", e);
				}
			}
		});
	}
	
	/**
	 * 结束监控
	 * @param monitorObj
	 */
	public void stopMonitor(final String monitorObj){
		if(!inMonitor(monitorObj)){
			return;
		}
		monitorMap.remove(getMonitorObj(monitorObj));
		
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					String realMonitorObj = getMonitorObj(monitorObj);
					
					BufferedWriter writer = writerMap.get(realMonitorObj);
					if(writer != null){
						try {
							writer.flush();
						}catch(Exception e){
						}
						try {
							writer.close();
						}catch(Exception e){
						}
					}
					writerMap.remove(realMonitorObj);
					
				}catch(Exception e){
					logger.log(Level.INFO, "", e);
				}
			}
		});
	}
	
	/**
	 * 是否已监控
	 * @param monitorObj
	 * @return
	 */
	public boolean inMonitor(String monitorObj){
		return monitorMap.containsKey(getMonitorObj(monitorObj));
	}
	
	/**
	 * 获取监控日志文件
	 * @param monitorObj
	 * @return
	 */
	private File getMonitorLog(String monitorObj){
		File file = new File(System.getProperty("java.io.tmpdir") + HOME_DIR + monitorObj + ".log");
		if(!file.exists()){
			file.getParentFile().mkdirs();
		}
		return file;
	}
	
	/**
	 * 获取监控日志文件，当监控停止且日志输出完毕后返回file对象，否则返回null
	 * @param monitorObj
	 * @return
	 */
	public File obtainMonitorLog(String monitorObj){
		monitorObj = getMonitorObj(monitorObj);
		if(writerMap.containsKey(monitorObj)){
			return null;
		}
		return getMonitorLog(monitorObj);
	}
	
	/**
	 * 检查是否存在超过时长的监控对象
	 */
	private void checkMonitors() {
		long now = new Date().getTime();
		if(now < checkTime){
			return;
		}
		setCheckTime();
		
		List<String> monitors = new ArrayList<String>();
		for (Entry<String, Date> entry : monitorMap.entrySet()) {
			if(entry.getValue().getTime() + EXPIRED < now){
				monitors.add(entry.getKey());
			}
		}
		for (String monitorObj : monitors) {
			stopMonitor(monitorObj);
		}
	}

	/**
	 * 获取监控对象名，当监控对象为null时，默认为ANONYMOUS
	 * @param monitorObj
	 * @return
	 */
	private String getMonitorObj(String monitorObj){
		return monitorObj == null || monitorObj.trim().equals("") ? ANONYMOUS : monitorObj;
	}
	
	/**
	 * 1/2失效时长过后校验
	 */
	private void setCheckTime(){
		checkTime = new Date().getTime() + (EXPIRED / 2);
	}

}
